package net.srcz.jsjvm2;

import java.io.PrintStream;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

// http://www.brics.dk/~mis/dOvs/jvmspec/ref-Java.html
public class JsMethodVisitor implements MethodVisitor {

	PrintStream ps;
	int openedLabels = 0;
	String name;
	String desc;
	
	public JsMethodVisitor(PrintStream ps, String name, String desc) {
		this.ps = ps;
		this.name = name;
		this.desc = desc;
	}

	@Override
	public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
		return null;
	}

	@Override
	public AnnotationVisitor visitAnnotationDefault() {
		return new JsAnnotationVisitor();
	}

	@Override
	public void visitAttribute(Attribute attr) {
	 	ps.println("attrib" + attr.type);
		
	}

	@Override
	public void visitCode() {
		ps.println("for(;;) {");
		ps.println("switch(nextLabel) {");
		ps.println("default:");
	}

	@Override
	public void visitEnd() {
		ps.println("}");
		ps.println("}");
	}

	@Override
	// GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD.
	public void visitFieldInsn(int opcode, String owner, String name,
			String desc) {
		ps.printf("%s(\"%s\",\"%s\",\"%s\");\n",OpCodesNames.get(opcode),owner,name,desc);
	}

	@Override
	public void visitFrame(int type, int nLocal, Object[] local, int nStack,
			Object[] stack) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void visitIincInsn(int var, int increment) {
		ps.printf("iinc(%d,%d);\n",var,increment);
	}

	@Override
	// NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, ICONST_3, 
	// ICONST_4, ICONST_5, LCONST_0, LCONST_1, FCONST_0, FCONST_1, FCONST_2, 
	// DCONST_0, DCONST_1, IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD,
	// CALOAD, SALOAD, IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, 
	// CASTORE, SASTORE, POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2,
	// SWAP, IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, 
	// IDIV, LDIV, FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL,
	// LSHL, ISHR, LSHR, IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, I2L, I2F, 
	// I2D, L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, I2C, I2S, LCMP, FCMPL, 
	// FCMPG, DCMPL, DCMPG, IRETURN, LRETURN, FRETURN, DRETURN, ARETURN, RETURN, 
	// ARRAYLENGTH, ATHROW, MONITORENTER, or MONITOREXIT
	public void visitInsn(int opcode) {
		ps.printf("%s();\n",OpCodesNames.get(opcode));
	}

	@Override
	// BIPUSH, SIPUSH, NEWARRAY
	public void visitIntInsn(int opcode, int operand) {
		ps.printf("%s(%d);\n",OpCodesNames.get(opcode),operand);
	}

	@Override
	// IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT,
	// IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL.
	public void visitJumpInsn(int opcode, Label label) {
		ps.printf("%s(%s);\n",OpCodesNames.get(opcode),label.toString().substring(1));
	}
	@Override
	public void visitLabel(Label label) {
		//ps.printf("this.\"%s%s %s();\n}\n",name,desc,label);
		//ps.printf("this.\"%s%s %s\" = function {\n",name,desc,label);
		ps.println(label.toString().substring(1)+": ");
		
		
	}

	@Override
	public void visitLdcInsn(Object cst) {
		ps.printf("ldcInsn(\"%s\");\n",cst);
	}

	@Override
	public void visitLineNumber(int line, Label start) {
		// TODO 
	}

	@Override
	public void visitLocalVariable(String name, String desc, String signature,
			Label start, Label end, int index) {
		//ps.println("localvar('"+name+"');");
		// replaceAll("local["+index+"]","local['"+name+"']", indexof start+":", indeox end+":");
	}

	@Override
	public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
		ps.printf("lookupSwitch(\"%s\",\"%s\",\"%s\");\n",dflt,keys,labels);
		
	}

	@Override
	public void visitMaxs(int maxStack, int maxLocals) {
		// TODO Auto-generated method stub
		
	}

	@Override
	// INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, INVOKEINTERFACE or INVOKEDYNAMIC.
	public void visitMethodInsn(int opcode, String owner, String name,
			String desc) {
		ps.printf("%s(\"%s\",\"%s%s\");\n", OpCodesNames.get(opcode), owner, name, desc);
	}

	@Override
	// MULTIANEWARRAY
	public void visitMultiANewArrayInsn(String desc, int dims) {
		ps.println("var array = new Array();");
		ps.println("var nb = stack.pop();");
		ps.println("for(i=0;i<nb;i++) {");
		createMultiArray("array[i]", 0, dims-1);
		ps.println("}");
		ps.println("stack.push(array);");
	}

	private void createMultiArray(String name, int pos, int dims) {
		char iter = (char)('j'+pos);
		ps.println(name+" = new Array();");
		ps.println("var nb"+pos+" = stack.pop();");
		ps.println("for("+iter+"=0;"+iter+"<nb"+pos+";"+iter+"++) {");
		if(dims == 0) {
			ps.println(name+"["+iter+"] = null;");
		} else {
			ps.println(name+"["+iter+"] = new Array();");
			createMultiArray(name+"["+iter+"]", pos+1, dims-1);
		}
		ps.println("}");
	}
	
	@Override
	public AnnotationVisitor visitParameterAnnotation(int parameter,
			String desc, boolean visible) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void visitTableSwitchInsn(int min, int max, Label dflt,
			Label[] labels) {
		ps.println("switch(stack.pop()) {");
		for(int i=0; i<=max-min; i++) {
			ps.println("case "+(min+i)+":");
			ps.println("continue "+labels[i]+";");
			ps.println("break;");
		}
		ps.println("default:");
		ps.println("continue "+dflt+";");
		ps.println("break;");
		ps.println("}");
	}

	@Override
	public void visitTryCatchBlock(Label start, Label end, Label handler,
			String type) {
		ps.println("visitTryCatchBlock "+start + " " + end + " " + handler + " " + type);
		// ps.replaceFirst(start+":",start+": try {"
		//ps.replaceFirst(end+":","} catch(e) { "
		//+ (type == null ? "" : "if(instanceof(e,"+type+") ") + continue "+handler+ "} "+end+":");
	}

	@Override
	// NEW, ANEWARRAY, CHECKCAST or INSTANCEOF.
	public void visitTypeInsn(int opcode, String type) {
		ps.printf("%s(\"%s\");\n",OpCodesNames.get(opcode),type);
	}

	@Override
	// ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET.
	public void visitVarInsn(int opcode, int var) {
		ps.printf("%s(%d);\n",OpCodesNames.get(opcode),var);
	}
	
}
